Counter Application in React JS
A Counter Application is a beginner-friendly React JS project used to understand state management, event handling, component rendering, and user interaction. It allows users to increase, decrease, and reset a numerical value using buttons. React's useState() Hook is commonly used to store and update the counter value.
Course: JustAcademy React JS Training Course
Course Demo: Register for React JS Course Demo
1. Learning Objectives
After completing this topic, learners will be able to:
- Understand the concept and purpose of a Counter Application.
- Create functional components in React JS.
- Use the
useState() Hook to manage counter values.
- Handle button clicks using event handlers.
- Increment, decrement, and reset a counter.
- Understand how state updates trigger component re-rendering.
- Apply conditional rendering and basic input validation.
- Build a simple interactive React JS project.
2. What Is a Counter Application?
A Counter Application is a small interactive application that displays a number and allows users to change that number through different actions. In React JS, the displayed number is generally stored in component state.
Example: A counter starts at 0. When the user clicks the Increase button, the value changes to 1. Clicking Increase again changes it to 2. The Decrease button reduces the value, and the Reset button returns it to 0.
Common Counter Operations
| Operation |
Description |
Example |
| Increment |
Increases the current count by one. |
0 → 1 |
| Decrement |
Decreases the current count by one. |
5 → 4 |
| Reset |
Returns the count to its initial value. |
8 → 0 |
| Custom Increment |
Increases the count by a selected amount. |
2 → 7 when adding 5 |
| Minimum Limit |
Prevents the count from going below a defined value. |
0 remains 0 |
3. Why Build a Counter Application in React?
A Counter Application helps beginners understand the fundamental features of React without needing a complex project.
- State management: Stores the current counter value.
- Event handling: Responds to user actions such as button clicks.
- Component-based development: Organizes the interface into reusable components.
- Dynamic UI updates: Displays the latest value when state changes.
- Conditional rendering: Displays messages based on the counter value.
- Practical learning: Builds a foundation for forms, shopping carts, dashboards, and other interactive applications.
4. Prerequisites
Before creating the Counter Application, learners should understand the following concepts:
- Basic HTML elements such as headings, buttons, and containers.
- CSS properties for layout, spacing, colors, and typography.
- JavaScript variables, functions, arrow functions, and event concepts.
- Basic React components and JSX syntax.
- Basic understanding of npm and a React development environment.
5. Understanding State in React JS
State is data that a React component remembers between renders. When state changes through its setter function, React schedules a re-render so the interface can display the updated value.
For a counter, the state variable stores the current number, and the setter function updates that number.
Example of useState()
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Count: {count}</h1>
</div>
);
}
export default Counter;
Explanation
| Code |
Meaning |
import { useState } from 'react'; |
Imports the React Hook used for state management. |
useState(0) |
Initializes the counter value to 0. |
count |
Contains the current counter value. |
setCount |
Updates the counter state. |
{count} |
Displays the current value in JSX. |
Important: Do not change the state variable directly, such as count = count + 1. Use the setter function, such as setCount(count + 1), so React can update the interface correctly.
6. Creating a Counter Application Step by Step
Step 1: Create a React Project
If you do not already have a React project, you can create one using Vite. Make sure Node.js and npm are installed.
npm create vite@latest counter-app -- --template react
cd counter-app
npm install
npm run dev
Run the command shown by Vite in your terminal and open the local URL it provides in your browser.
Step 2: Understand the Project Structure
counter-app/
├── public/
├── src/
│ ├── App.jsx
│ ├── App.css
│ ├── index.css
│ └── main.jsx
├── package.json
└── index.html
File responsibilities:
App.jsx contains the Counter Application component.
App.css contains styles for the counter interface.
index.css contains global styles.
main.jsx renders the React application into the page.
package.json stores project dependencies and scripts.
Step 3: Write the Counter Component
Replace the content of src/App.jsx with the following code:
import { useState } from 'react';
import './App.css';
function App() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(previousCount => previousCount + 1);
};
const decrement = () => {
setCount(previousCount => previousCount - 1);
};
const reset = () => {
setCount(0);
};
return (
<main className="counter-container">
<h1>Counter Application</h1>
<p className="description">
Manage your counter with simple buttons.
</p>
<div className="counter-value" aria-live="polite">
{count}
</div>
<div className="button-group">
<button onClick={decrement}>Decrease</button>
<button onClick={reset}>Reset</button>
<button onClick={increment}>Increase</button>
</div>
<p className="status">
{count > 0
? 'The count is positive.'
: count < 0
? 'The count is negative.'
: 'The count is zero.'}
</p>
</main>
);
}
export default App;
Step 4: Add CSS Styling
Replace the content of src/App.css with the following styles:
.counter-container {
max-width: 480px;
margin: 60px auto;
padding: 32px 24px;
text-align: center;
font-family: Arial, sans-serif;
background: #ffffff;
border-radius: 16px;
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.12);
}
.counter-container h1 {
margin-bottom: 8px;
color: #1f2937;
}
.description {
color: #6b7280;
}
.counter-value {
margin: 28px 0;
font-size: 64px;
font-weight: bold;
color: #2563eb;
}
.button-group {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
}
.button-group button {
padding: 12px 18px;
border: none;
border-radius: 8px;
background: #2563eb;
color: #ffffff;
font-size: 15px;
cursor: pointer;
}
.button-group button:hover {
background: #1d4ed8;
}
.button-group button:focus-visible {
outline: 3px solid #93c5fd;
outline-offset: 3px;
}
.status {
margin-top: 22px;
color: #374151;
}
@media (max-width: 480px) {
.counter-container {
margin: 24px 12px;
padding: 24px 16px;
}
.counter-value {
font-size: 52px;
}
}
Step 5: Check Global CSS
In src/index.css, you can use the following minimal global styles:
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background: #f3f4f6;
}
Step 6: Run and Test the Application
- Start the development server using
npm run dev.
- Open the local URL displayed in the terminal.
- Click Increase and verify that the number increases.
- Click Decrease and verify that the number decreases.
- Click Reset and verify that the number returns to 0.
- Check that the status message changes based on the current value.
7. How the Counter Application Works
The application follows a simple state-update cycle:
User clicks a button
↓
React event handler runs
↓
State setter updates the count
↓
React schedules a re-render
↓
Updated count appears in the UI
For example, when the count is 2 and the user clicks Increase, the updater function receives the previous value of 2 and returns 3. React then renders the component with the new count.
8. Understanding Increment, Decrement, and Reset
Increment
const increment = () => {
setCount(previousCount => previousCount + 1);
};
This function increases the previous count by one. The updater form is useful when the new state depends on the previous state.
Decrement
const decrement = () => {
setCount(previousCount => previousCount - 1);
};
This function decreases the previous count by one. It allows negative values unless a minimum limit is added.
Reset
const reset = () => {
setCount(0);
};
This function resets the counter to its initial value of zero.
Prevent the Counter from Going Below Zero
If the application should never display a negative value, use a minimum limit:
const decrement = () => {
setCount(previousCount =>
Math.max(0, previousCount - 1)
);
};
With this version, clicking Decrease while the count is 0 keeps the value at 0.
9. Using an Updater Function vs. Direct State Updates
React state behaves like a snapshot for the current render. Calling a state setter does not immediately change the variable inside the already-running handler. When the next value depends on the previous value, an updater function is a reliable choice.
| Approach |
Example |
When to Use |
| Direct update |
setCount(count + 1) |
Suitable for a simple update based on the current render's value. |
| Updater function |
setCount(c => c + 1) |
Useful when the new value depends on the previous state or multiple updates are queued. |
| Reset to fixed value |
setCount(0) |
Useful when resetting to a known value. |
Example: Increase the Count Three Times
const increaseThreeTimes = () => {
setCount(c => c + 1);
setCount(c => c + 1);
setCount(c => c + 1);
};
Using updater functions allows React to process each queued update based on the result of the previous update. If the current count is 0, this handler increases it to 3.
10. Adding a Custom Increment Value
A more advanced counter can allow users to choose how much to add or subtract. This example uses a fixed increment of 5.
const incrementByFive = () => {
setCount(c => c + 5);
};
const decrementByFive = () => {
setCount(c => c - 5);
};
You can also store the custom amount in a separate state variable and allow the user to change it through an input field.
11. Creating a Reusable Counter Component
React components help you reuse counter logic in different parts of an application. Each independently rendered Counter component can maintain its own state.
import { useState } from 'react';
function Counter({ initialValue = 0, title = 'Counter' }) {
const [count, setCount] = useState(initialValue);
return (
<section>
<h2>{title}</h2>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>
Increase
</button>
<button onClick={() => setCount(c => c - 1)}>
Decrease
</button>
<button onClick={() => setCount(initialValue)}>
Reset
</button>
</section>
);
}
export default function App() {
return (
<div>
<Counter title="Visitors" initialValue={0} />
<Counter title="Points" initialValue={10} />
</div>
);
}
Note: initialValue is used to initialize the state. If the prop later changes, the existing state does not automatically reset. If that behavior is required, design an explicit reset or controlled-state pattern.
12. Counter Application with a Custom Increment Input
The following example extends the counter by allowing users to enter a custom step value. It includes input validation and a reset button.
import { useState } from 'react';
export default function App() {
const [count, setCount] = useState(0);
const [step, setStep] = useState('1');
const parsedStep = Number(step);
const validStep =
step.trim() !== '' &&
Number.isFinite(parsedStep) &&
parsedStep > 0;
return (
<main className="counter-container">
<h1>Smart Counter</h1>
<p>Current count: {count}</p>
<label htmlFor="step">Step value</label>
<input
id="step"
type="number"
min="1"
value={step}
onChange={event => setStep(event.target.value)}
/>
<div className="button-group">
<button
disabled={!validStep}
onClick={() => setCount(c => c + parsedStep)}
>
Increase
</button>
<button
disabled={!validStep}
onClick={() => setCount(c => c - parsedStep)}
>
Decrease
</button>
<button onClick={() => setCount(0)}>
Reset
</button>
</div>
{!validStep && (
<p role="alert">Enter a number greater than zero.</p>
)}
</main>
);
}
This example demonstrates multiple state variables, controlled inputs, input validation, conditional rendering, and functional state updates.
13. Important React Concepts Used in a Counter
| Concept |
Purpose in the Counter Application |
| Functional Component |
Defines the interface and its behavior. |
| JSX |
Describes the elements displayed in the browser. |
useState() |
Stores the current counter value. |
| Event Handler |
Responds to button clicks. |
| State Setter |
Requests an update to the state. |
| Re-rendering |
Allows the UI to display updated state. |
| Props |
Pass initial values or labels into reusable counters. |
| Conditional Rendering |
Shows messages according to the current count. |
| Controlled Input |
Keeps an input value synchronized with React state. |
| CSS |
Styles the counter layout and buttons. |
14. Common Mistakes and Their Solutions
| Mistake |
Why It Is a Problem |
Solution |
| Changing the state variable directly |
React may not re-render based on a direct mutation. |
Use the state setter function. |
| Calling a handler while rendering |
onClick={increment()} runs the function during rendering. |
Use onClick={increment}. |
| Forgetting to import useState |
The Hook will be undefined in the component. |
Import it from React. |
| Using the wrong initial value |
The counter begins at an unintended number. |
Set the required initial value, such as useState(0). |
| Allowing unwanted negative values |
The counter may go below zero. |
Use Math.max(0, count - 1) logic in the updater. |
| Expecting state to change immediately inside a handler |
The current render's state value remains a snapshot. |
Use updater functions when calculations depend on previous state. |
| Using invalid input values |
A custom step can be empty or invalid. |
Validate the input before updating the count. |
15. Best Practices
- Use descriptive names such as
count, setCount, increment, and reset.
- Use functional state updates when the next count depends on the previous count.
- Keep components small and focused on one responsibility.
- Use semantic HTML elements such as
main, section, and button.
- Provide visible focus indicators so the interface is usable with a keyboard.
- Validate custom increment values before applying them.
- Use
aria-live where helpful to announce changes to assistive technology.
- Keep state updates inside event handlers rather than changing state during ordinary rendering.
- Test increment, decrement, reset, limits, and invalid input.
- Separate styling into CSS files to make the component easier to maintain.
16. Testing Checklist
- ☐ The initial count is 0.
- ☐ Increase changes the count by one.
- ☐ Decrease changes the count by one in the negative direction.
- ☐ Reset returns the count to 0.
- ☐ The status message reflects the count correctly.
- ☐ The minimum limit works if enabled.
- ☐ A custom step value works correctly.
- ☐ Empty or invalid step values are handled safely.
- ☐ Buttons are usable with a keyboard.
- ☐ The layout remains readable on small screens.
17. Mini Project Enhancement Ideas
After building the basic Counter Application, try adding these features:
- Minimum and maximum limits: Prevent the counter from going below 0 or above 100.
- Custom step size: Allow the user to increase or decrease by a chosen amount.
- Dark mode: Add a button to switch between light and dark themes.
- Multiple counters: Display independent counters for different tasks.
- History: Show the previous counter values or actions.
- Undo: Restore the previous value after a counter update.
- Local storage: Save the counter value so it can be restored after a page refresh.
- Unit tests: Verify that increment, decrement, and reset work as expected.
- Responsive design: Improve the layout for mobile, tablet, and desktop screens.
18. Interview Questions and Answers
Q1. What is a Counter Application in React JS?
Answer: It is a simple interactive application that displays a numerical value and allows users to increase, decrease, or reset it using React components and event handlers.
Q2. Which Hook is commonly used to create a counter?
Answer: The useState() Hook is commonly used to store and update the counter value.
Q3. What does useState(0) mean?
Answer: It initializes a state variable with the value 0 and provides a setter function for updating that state.
Q4. What is the purpose of setCount()?
Answer: It requests a state update. React can then render the component again to display the updated count.
Q5. Why should state not be modified directly?
Answer: React relies on state setter functions to schedule updates correctly. Directly changing a variable does not reliably update the displayed UI.
Q6. What is an event handler?
Answer: An event handler is a function that responds to an event, such as clicking the Increase button.
Q7. What is the difference between state and props?
Answer: State is managed within a component using React features such as useState(). Props are values passed from a parent component to a child component.
Q8. Why use setCount(c => c + 1)?
Answer: It calculates the next value from the previous state, making it useful when multiple updates may be queued.
Q9. How can you prevent a counter from going below zero?
Answer: Use an updater such as setCount(c => Math.max(0, c - 1)).
Q10. What is re-rendering in React?
Answer: Re-rendering means React calls a component again to calculate its updated UI when relevant state or props change.
Q11. Can an application have multiple counters?
Answer: Yes. Each separately rendered Counter component can maintain independent state, or a parent component can manage shared state when the counters need to coordinate.
Q12. How can you test a Counter Application?
Answer: Verify the initial value, increment and decrement behavior, reset behavior, boundary conditions, invalid inputs, and keyboard accessibility.
19. Practice Exercises
- Create a counter that starts at 10.
- Add a button that increases the count by 5.
- Prevent the count from becoming negative.
- Display a message when the count reaches 50.
- Add a custom input for selecting the increment value.
- Create two independent Counter components on the same page.
- Add an Undo button that restores the previous count.
- Save and restore the counter value using local storage.
- Style the application with responsive CSS.
- Write tests for increment, decrement, and reset behavior.
20. Quick Revision
| Topic |
Key Point |
| Counter Application |
Displays and updates a numerical value. |
| State |
Stores the current counter value. |
useState() |
Provides the current state and its setter. |
| Increment |
Increases the count. |
| Decrement |
Decreases the count. |
| Reset |
Restores the counter to its initial value. |
| Event Handling |
Connects button clicks to functions. |
| Re-rendering |
Updates the UI after state changes. |
| Functional Updater |
Calculates the next state from the previous state. |
21. Official Documentation and Learning Resources
22. Conclusion
A Counter Application is an excellent beginner project for learning React JS fundamentals. By creating increment, decrement, and reset buttons, learners practice functional components, JSX, the useState() Hook, event handling, and dynamic UI updates. Once the basic application works, adding custom step values, validation, multiple counters, accessibility features, and tests provides a pathway toward building more advanced React applications.
To learn more, visit the JustAcademy React JS Training Course or register for a React JS course demo.